from django.shortcuts import render
from django.views import View
from django_redis import get_redis_connection
from django import http
from django.utils import timezone
from django.db import transaction

from decimal import Decimal
import json

from users.models import Address
from goods.models import SKU
from meiduo_mall.utils.views import LoginRequiredMixin, LoginRequiredJSONMixin
from meiduo_mall.utils.response_code import RETCODE
from orders.models import OrderInfo, OrderGoods

# Create your views here.


class OrderSuccessView(LoginRequiredMixin, View):
    """提交订单成功页面"""

    def get(self, request):
        """提交订单成功页面"""
        # 接受参数
        order_id = request.GET.get('order_id')
        payment_amount = request.GET.get('payment_amount')
        pay_method = request.GET.get('pay_method')

        # 构造上下文
        context = {
            'order_id': order_id,
            'payment_amount': payment_amount,
            'pay_method': pay_method,
        }

        return render(request, 'order_success.html', context)


class OrderCommitView(LoginRequiredJSONMixin, View):
    """提交订单"""

    def post(self, request):
        """保存订单信息和订单商品信息"""
        # 获取当前要保存的订单数据
        json_dict = json.loads(request.body.decode())
        address_id = json_dict.get('address_id')
        pay_method = json_dict.get('pay_method')
        # 校验参数
        if not all([address_id, pay_method]):
            return http.HttpResponseForbidden('缺少必传参数')
        # 判断address_id是否合法
        try:
            address = Address.objects.get(id=address_id)
        except Exception:
            return http.HttpResponseForbidden('参数address_id错误')
        # 判断pay_method是否合法
        if pay_method not in [OrderInfo.PAY_METHODS_ENUM['CASH'], OrderInfo.PAY_METHODS_ENUM['ALIPAY']]:
            return http.HttpResponseForbidden('参数pay_method错误')

        # 获取登录用户
        user = request.user
        # 生成订单编号：年月日时分秒+用户编号 2022715211110000000100
        order_id = timezone.localtime().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)
        # 开启一个事物
        with transaction.atomic():
            # 创建事物保存点
            save_id = transaction.savepoint()
            # 回滚
            try:
                # 保存订单基本信息 OrderInfo
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal(0),
                    freight=Decimal('10.00'),
                    pay_method=pay_method,
                    status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY'] else OrderInfo.ORDER_STATUS_ENUM['UNSEND']
                )

                # 从redis读取购物车中被选中的商品信息
                redis_conn = get_redis_connection('carts')
                # 所有的购物车数据，包含了勾选和未勾选
                redis_cart = redis_conn.hgetall('carts_%s' % user.id)
                # 被勾选的商品
                redis_selected = redis_conn.smembers('selected_%s' % user.id)
                # 构造购物车中被勾选的商品数据
                new_cart_dict = {}
                for sku_id in redis_selected:
                    new_cart_dict[int(sku_id)] = int(redis_cart[sku_id])
                # 获取被勾选的商品的sku_id
                sku_ids = new_cart_dict.keys()

                # 每个商品都有多次下单的机会，直到库存不足
                while True:
                    # 查询SKU信息
                    sku = SKU.objects.get(id=sku_id)
                    # 获取原始库存和销量
                    origin_stock = sku.stock
                    origin_sales = sku.sales
                    # 判断库存
                    sku_count = new_cart_dict[sku.id]
                    # 判断商品数据量是否大于库存，如果大于，响应 库存不足
                    if sku_count > origin_stock:
                        # 出错就回滚
                        transaction.savepoint_rollback(save_id)
                        return http.JsonResponse({'code': RETCODE.STOCKERR, 'errmsg': '库存不足'})

                    # SKU减少库存，增加销量
                    # sku.stock -= sku_count
                    # sku.sales += sku_count
                    # sku.save()
                    new_stock = origin_stock - sku_count
                    new_sales = origin_sales + sku_count
                    result = SKU.objects.filter(id=sku_id,stock=origin_stock).update(stock=new_stock, sales=new_sales)
                    # 如果在更新数据时，原始数据变化了，返回0，表示有资源抢夺
                    if result == 0:
                        print('下单失败')
                        continue
                    # 修改SPU销量
                    sku.spu.sales += sku_count
                    sku.spu.save()

                    # 保存订单商品信息
                    OrderGoods.objects.create(
                        order=order,
                        sku=sku,
                        count=sku_count,
                        price=sku.price,
                    )

                    # 保存商品订单中总结和总数量
                    order.total_count += sku_count
                    order.total_amount += (sku_count * sku.price)
                    # 下单成功，记得break
                    break
                # 添加邮费和保存订单信息
                order.total_amount += order.freight
                order.save()
            except Exception as e:
                transaction.savepoint_rollback(save_id)
                return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '下单失败'})

            # 提交订单成功，提交一次事物
            transaction.savepoint_commit(save_id)

        # 清除购物车中已结算商品
        pl = redis_conn.pipeline()
        pl.hdel('carts_%s' % user.id, *redis_cart)
        pl.srem('selected_%s' % user.id, *redis_selected)
        pl.execute()

        # 响应提交订单结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '下单成功', 'order_id': order.order_id})


class OrderSettlementView(LoginRequiredMixin, View):
    """结算订单"""

    def get(self, request):
        """查询并展示要结算的订单数据"""
        # 获取登录用户
        user = request.user
        #  查询用户收货地址：查询登录用户的没有被删除的收货地址
        try:
            addresses = Address.objects.filter(user=user, is_deleted=False)
        except Exception as e:
            # 如果没有查询出地址，可以去编辑收货地址
            addresses = None

        # 查询redis购物车中被勾选的商品
        redis_conn = get_redis_connection('carts')
        # 所有的购物车数据，包含了勾选和未勾选
        redis_cart = redis_conn.hgetall('carts_%s' % user.id)
        # 被勾选的商品
        redis_selected = redis_conn.smembers('selected_%s' % user.id)
        # 构造购物车中被勾选的商品数据
        new_cart_dict = {}
        for sku_id in redis_selected:
            new_cart_dict[int(sku_id)] = int(redis_cart[sku_id])

        # 获取被勾选的商品的sku_id
        sku_ids = new_cart_dict.keys()
        skus = SKU.objects.filter(id__in=sku_ids)

        total_count = 0
        total_amount = Decimal(0.00)
        # 取出所有的sku
        for sku in skus:
            # 遍历skus给每个sku补充count和amount
            sku.count = new_cart_dict[sku.id]
            sku.amount = sku.price * sku.count

            # 累加数据和金额
            total_count += sku.count
            total_amount += sku.amount

        # 指定默认邮费
        freight = Decimal(10.00)

        # 构造上下文
        context = {
            'addresses': addresses,
            'skus': skus,
            'total_count': total_count,
            'total_amount': total_amount,
            'freight': freight,
            'payment_amount': total_amount + freight,
        }

        return render(request, 'place_order.html', context)